/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package dev.ahamed.mva.sample.view.widget;

import dev.ahamed.mva.sample.view.impl.ISwipeToDismissListener;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.ScrollHelper;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.multimodalinput.event.TouchEvent;

/**
 * SwipeLayout
 *
 * @since 2021.07.08
 */
public class SwipeLayout extends ComponentContainer implements Component.TouchEventListener {
    private static final int SWIPE_DISMISS_DISTANCE = 200;

    /**
     * 最小阻尼，用户越往下拉，越不跟手
     */
    private float minDamp = 1.6f;
    /**
     * 最大阻尼
     */
    private float maxDamp = 2.2f;
    private int mLastX;
    private AutoScroller mAutoScroll;
    private ISwipeToDismissListener mSwipeToDismissListener;
    private int mItemPosition = 1;
    private GestureDetector mGestureDetector;
    private boolean mScrollEnable = true;

    public SwipeLayout(Context context) {
        this(context, null);
    }

    public SwipeLayout(Context context, AttrSet attrSet) {
        this(context, attrSet, "");
    }

    public SwipeLayout(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        // 调用setTouchEventListener，让onTouchEvent方法执行
        setTouchEventListener(this);
        mGestureDetector = new GestureDetector(gestureDetector);
        mAutoScroll = new AutoScroller(this);
    }

    /**
     * 设置能否左右滑动
     *
     * @param scrollEnable 是否能左右滑动
     */
    public void setScrollEnable(boolean scrollEnable) {
        this.mScrollEnable = scrollEnable;
    }

    /**
     * 设置滑动删除监听
     *
     * @param swipeToDismissListener 滑动监听
     * @param itemPosition item位置
     */
    public void setSwipeToDismissListener(ISwipeToDismissListener swipeToDismissListener, int itemPosition) {
        this.mSwipeToDismissListener = swipeToDismissListener;
        this.mItemPosition = itemPosition;
    }

    private final GestureDetector.OnGestureListener gestureDetector = new GestureDetector.OnGestureListener() {
        @Override
        public boolean onScroll(TouchEvent e1, TouchEvent e2, float distanceX, float distanceY) {
            // 查找可以滚动的组件
            Component child = getComponentAt(0);

            int offsetX;
            if (child.getMarginLeft() < SWIPE_DISMISS_DISTANCE) {
                // 没到可刷新的距离，减少阻尼
                offsetX = (int) (mLastX / minDamp);
            } else {
                // 达到可刷新的距离，加大阻尼
                offsetX = (int) (mLastX / maxDamp);
            }
            // 如果是正在刷新状态，则不允许在滑动的时候改变状态
            // 用户手动下拉触发滑动，moveDown方法的第二个参数传false
            boolean bool = moveDown(offsetX, false);
            mLastX = (int) -distanceX;
            return bool;
        }
    };

    /**
     * 根据偏移量滚动头部组件和子组件
     *
     * @param offsetX 偏移量
     * @param auto false手动触发滚动,true自动触发滚动
     * @return 默认值true
     */
    private boolean moveDown(int offsetX, boolean auto) {
        Component child = getComponentAt(0);
        offsetLeftAndRight(child, offsetX);
        return true;
    }

    /**
     * 修改组件的位置来达到滚动的效果，只修改组件的水平位置
     *
     * @param component 控件对象
     * @param offset 偏移距离
     */
    public void offsetLeftAndRight(Component component, int offset) {
        component.setComponentPosition(component.getLeft() + offset,
                component.getTop(),
                component.getRight() + offset,
                component.getBottom());
    }

    /**
     * 处理分派给组件的触摸事件，要想该方法执行，需要调用setTouchEventListener方法，这里在构造方法里面调用了setTouchEventListener方法
     *
     * @param component 控件对象
     * @param ev TouchEvent对象
     * @return 处理分派给组件的触摸事件
     */
    @Override
    public boolean onTouchEvent(Component component, TouchEvent ev) {
        if (!mScrollEnable) {
            return true;
        }
        Component child = getComponentAt(0);
        if (ev.getAction() == TouchEvent.CANCEL ||
                ev.getAction() == TouchEvent.PRIMARY_POINT_UP) {
            // 松开手
            mLastX = 0;
            if (child.getContentPositionX() != 0) {
                recover((int) child.getContentPositionX());
                return false;
            }
        }
        // 处理完抬起事件后，将手势交给手势处理器
        boolean consumed = mGestureDetector.onTouchEvent(ev);
        if (consumed) {
            // 消费事件，事件不再往下传递
            return false;
        } else {
            return true;
        }
    }

    private int getScreenWidth() {
        return DisplayManager.getInstance().getDefaultDisplay(getContext()).get().getRealAttributes().width;
    }

    private void recover(int dis) {
        if (Math.abs(dis) >= SWIPE_DISMISS_DISTANCE) {
            if (dis < 0) {
                mAutoScroll.recover(getScreenWidth() + dis);
            } else {
                mAutoScroll.recover(-(getScreenWidth() - dis));
            }
            if (mSwipeToDismissListener != null) {
                mSwipeToDismissListener.onItemDismissed(mItemPosition, null);
            }
        } else {
            mAutoScroll.recover(dis);
        }
    }

    private static class AutoScroller implements Runnable {
        private final ScrollHelper mScroller;
        private int mLastX;

        private EventHandler mEventHandler;
        private SwipeLayout mSwipeLayout;

        AutoScroller(SwipeLayout swipeLayout) {
            mSwipeLayout = swipeLayout;
            mScroller = new ScrollHelper();
            mEventHandler = new EventHandler(EventRunner.getMainEventRunner());
        }

        @Override
        public void run() {
            if (!mScroller.isFinished()) {
                mScroller.updateScroll();
                mSwipeLayout.moveDown(mLastX - mScroller.getCurrValue(ScrollHelper.AXIS_X), true);
                mLastX = mScroller.getCurrValue(ScrollHelper.AXIS_X);
                mEventHandler.postTask(this);
            } else {
                mEventHandler.removeTask(this);
            }
        }

        void recover(int dis) {
            mEventHandler.removeTask(this);
            mLastX = 0;
            mScroller.startScroll(dis, 0, 0, 0);
            mEventHandler.postTask(this);
        }

    }
}
